<template>
  <div class="connect4">
    <header class="topbar">
      <h2>⭕ Connect Four 四子棋</h2>
      <div class="status">
        <span v-if="!gameOver">当前玩家：<strong :class="currentPlayer === 1 ? 'p1' : 'p2'">{{ currentPlayer === 1 ? '红' : '黄' }}</strong></span>
        <span v-else>
          <template v-if="winner === 1">胜者：<strong class="p1">红</strong></template>
          <template v-else-if="winner === 2">胜者：<strong class="p2">黄</strong></template>
          <template v-else>平局</template>
        </span>
      </div>
      <div class="actions">
        <button class="btn" @click="newGame">新开一局</button>
        <button class="btn outline" @click="toggleHelp">玩法说明</button>
      </div>
    </header>

    <div class="board-wrapper">
      <!-- 头部投放区域（点击某列下落棋子） -->
      <div class="drop-row" role="group" aria-label="选择列投放棋子">
        <button
          v-for="c in cols"
          :key="'drop-'+c"
          class="drop-btn"
          :disabled="gameOver || columnFull(c-1)"
          @click="drop(c-1)"
          :title="columnFull(c-1) ? '该列已满' : '点击此列投放棋子'"
        >↓</button>
      </div>

      <!-- 棋盘 -->
      <div class="board" :style="gridStyle">
        <div
          v-for="(cell, idx) in flatBoard"
          :key="idx"
          class="cell"
        >
          <div
            class="disc"
            :class="discClass(cell.r, cell.c)"
            @click="onCellClick(cell.c)"
            :title="gameOver ? '已结束' : '点击此列投放棋子'"
          />
        </div>
      </div>
    </div>

    <div v-if="showHelp" class="help">
      <p>规则：轮流在 7 列 x 6 行的棋盘上投放棋子，使自己的颜色在任意方向（水平、垂直、斜线）形成连续 4 个即可获胜。</p>
      <ul>
        <li>点击上方箭头或棋盘任意格所在列进行投放。</li>
        <li>红先手，之后红黄轮流下。</li>
        <li>某列满格将不可投放。</li>
        <li>点击“新开一局”重置棋盘。</li>
      </ul>
    </div>

    <div v-if="gameOver" class="overlay">
      <div class="modal">
        <h3 v-if="winner === 1"><span class="p1">红</span> 获胜！</h3>
        <h3 v-else-if="winner === 2"><span class="p2">黄</span> 获胜！</h3>
        <h3 v-else>平局</h3>
        <button class="btn" @click="newGame">再来一局</button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue'

const rows = 6
const cols = 7

const board = ref(Array.from({ length: rows }, () => Array(cols).fill(0))) // 0 空，1 红(p1)，2 黄(p2)
const currentPlayer = ref(1)
const winner = ref(0)
const gameOver = ref(false)
const showHelp = ref(false)
const winningSet = ref(new Set())

const flatBoard = computed(() => {
  const arr = []
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      arr.push({ r, c, v: board.value[r][c] })
    }
  }
  return arr
})

const gridStyle = computed(() => ({
  gridTemplateColumns: `repeat(${cols}, 1fr)`,
  gridTemplateRows: `repeat(${rows}, 1fr)`,
}))

function toggleHelp() {
  showHelp.value = !showHelp.value
}

function keyOf(r, c) {
  return `${r}-${c}`
}

function newGame() {
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      board.value[r][c] = 0
    }
  }
  currentPlayer.value = 1
  winner.value = 0
  gameOver.value = false
  winningSet.value = new Set()
}

function columnFull(c) {
  return board.value[0][c] !== 0
}

function onCellClick(col) {
  if (gameOver.value) return
  drop(col)
}

function drop(col) {
  if (gameOver.value) return
  // 找到最底部空位
  let placedRow = -1
  for (let r = rows - 1; r >= 0; r--) {
    if (board.value[r][col] === 0) {
      board.value[r][col] = currentPlayer.value
      placedRow = r
      break
    }
  }
  if (placedRow === -1) return // 该列已满

  // 检查胜利
  const winCells = checkWinFrom(placedRow, col, currentPlayer.value)
  if (winCells.length >= 4) {
    winner.value = currentPlayer.value
    gameOver.value = true
    winningSet.value = new Set(winCells.map(([r, c]) => keyOf(r, c)))
    return
  }

  // 检查平局
  if (isDraw()) {
    winner.value = 0
    gameOver.value = true
    return
  }

  // 轮转玩家
  currentPlayer.value = currentPlayer.value === 1 ? 2 : 1
}

function isDraw() {
  for (let c = 0; c < cols; c++) {
    if (board.value[0][c] === 0) return false
  }
  return true
}

function checkWinFrom(r, c, player) {
  // 四个方向：水平、垂直、斜右下、斜左下
  const dirs = [
    [0, 1],
    [1, 0],
    [1, 1],
    [1, -1],
  ]

  for (const [dr, dc] of dirs) {
    const cells = collectLine(r, c, dr, dc, player)
    if (cells.length >= 4) return cells
  }
  return []
}

function collectLine(r, c, dr, dc, player) {
  const line = [[r, c]]
  // 正向
  let rr = r + dr
  let cc = c + dc
  while (inBounds(rr, cc) && board.value[rr][cc] === player) {
    line.push([rr, cc])
    rr += dr
    cc += dc
  }
  // 反向
  rr = r - dr
  cc = c - dc
  while (inBounds(rr, cc) && board.value[rr][cc] === player) {
    line.unshift([rr, cc])
    rr -= dr
    cc -= dc
  }
  return line
}

function inBounds(r, c) {
  return r >= 0 && r < rows && c >= 0 && c < cols
}

function discClass(r, c) {
  const v = board.value[r][c]
  return [
    v === 1 ? 'p1' : v === 2 ? 'p2' : 'empty',
    winningSet.value.has(keyOf(r, c)) ? 'win' : '',
  ]
}
</script>

<style scoped>
.connect4 {
  max-width: 720px;
  margin: 24px auto;
  padding: 12px 16px;
}
.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.topbar h2 { margin: 0; font-weight: 600; }
.status { font-size: 14px; }
.p1 { color: #e74c3c; }
.p2 { color: #f1c40f; }
.actions { display: flex; gap: 8px; }
.btn {
  padding: 6px 12px;
  border-radius: 6px;
  border: none;
  background: #3b82f6;
  color: #fff;
  cursor: pointer;
}
.btn:hover { filter: brightness(1.05); }
.btn.outline {
  background: transparent;
  color: #3b82f6;
  border: 1px solid #3b82f6;
}

.board-wrapper { margin-top: 12px; }
.drop-row {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 8px;
  margin-bottom: 10px;
}
.drop-btn {
  padding: 4px 0;
  border-radius: 6px;
  border: 1px solid #94a3b8;
  background: #f8fafc;
  cursor: pointer;
}
.drop-btn:disabled { cursor: not-allowed; opacity: 0.5; }

.board {
  display: grid;
  gap: 8px;
  background: #0ea5e9;
  padding: 10px;
  border-radius: 10px;
  box-shadow: inset 0 0 0 2px rgba(0,0,0,0.05);
}
.cell {
  display: flex;
  align-items: center;
  justify-content: center;
}
.disc {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  box-shadow: inset 0 0 0 2px rgba(0,0,0,0.05);
  background: #e2e8f0; /* empty */
}
.disc.empty { background: #e2e8f0; }
.disc.p1 { background: #ef4444; }
.disc.p2 { background: #f59e0b; }
.disc.win { outline: 3px solid #22c55e; }

.help {
  margin-top: 12px;
  padding: 8px 12px;
  border: 1px dashed #94a3b8;
  border-radius: 8px;
  background: #f8fafc;
  font-size: 14px;
}

.overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.4);
  display: flex;
  align-items: center;
  justify-content: center;
}
.modal {
  background: #fff;
  padding: 16px 20px;
  border-radius: 10px;
  min-width: 260px;
  text-align: center;
}
</style>